//
// Created by ta&to on 2022/8/16.
//

#include "http_socket.h"
/**
 * 创建connect 封装
 * @param logt
 * @param socket_fd 标识一个未连接socket
 * @param s_addr 指向要连接套接字的sockaddr结构体的指针
 * @return
 */
static inline int __socket_connect(log_t *logt, int socket_fd, const struct sockaddr_in* s_addr) {
    int ret = 0;
    socketNonBlock(logt, socket_fd);
    if((ret = connect(socket_fd, (struct sockaddr *)s_addr, sizeof(struct sockaddr_in))) == 0) {
        error(logt,"socket_connect to create a HTTP connect whit server immediately:[%s]", strerror(errno));
        return FALSE;
    }
    if(ret == -1 && errno != EINPROGRESS) {
        error(logt,"socket_connect to create a HTTP connect fd failure:[%s]", strerror(errno));
        return FALSE;
    }
    return TRUE;
}
/**
 * 创建socket 封装
 * @param logt
 * @param __family  AF_INET代表IPv4, AF_INET6代表IPv6, AF_UNIX代表Unix Domain Socket(本地文件)
 * @param __type
 detail:    SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流，使用带外数据传送机制，为Internet地址族使用TCP。
            SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小（通常很小）缓冲区的数据报服务，为Internet地址族使用UDP。
            SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口，在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接，连接成功后，即可用send()和recv()传送数据。当会话结束后，调用close()。带外数据根据规定用send()和recv()来接收。
            实现SOCK_STREAM类型套接口的通讯协议保证数据不会丢失也不会重复。如果终端协议有缓冲区空间，且数据不能在一定时间成功发送，则认为连接中断，其后续的调用也将以WSAETIMEOUT错误返回。
            SOCK_DGRAM类型套接口允许使用sendto()和recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接，则可用send()和recv()与该端口进行数据报的发送与接收。
 * @param __protocol
 * @return
 */
static inline int __socket_create(log_t *logt, int __family, int __type, int __protocol) {
    int socket_fd;
    if((socket_fd = socket(__family, __type, __protocol)) < 0) {
        error(logt,"socket_server to create a HTTP socket fd failure:[%s]", strerror(errno));
        return FALSE;
    }
    return socket_fd;
}
/**
 * socket 属性处理封装
 * @param logt
 * @param __socket_fd
 * @param __level
 * @param __optname
 * @param optval 指针，指向存放选项待设置的新值的缓冲区
 details:   SO_BINDTODEV char * 将套接字绑定到指定端口。
            SO_BROADCAST BOOL 允许套接口传送广播信息。
            SO_DEBUG BOOL 记录调试信息。
            SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。
            SO_DONTROUTE BOOL 禁止选径；直接传送。
            SO_KEEPALIVE BOOL 发送“保持活动”包。
            SO_LINGER struct linger FAR* 如关闭时有未发送数据，则逗留。
            SO_OOBINLINE BOOL 在常规数据流中接收带外数据。
            SO_RCVBUF int 为接收确定缓冲区大小。
            SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑（参见bind()）。
            SO_SNDBUF int 指定发送缓冲区大小。
            TCP_NODELAY BOOL 禁止发送合并的Nagle算法。
 * @param __optlen 缓冲区长度
 * @return
 */
static inline int __socket_option(log_t *logt, int __socket_fd, int __level, int __optname, const void *optval,
                                  socklen_t __optlen) {
    int ret;
    if(__socket_fd < 0)  return -1;
    if((ret = setsockopt(__socket_fd, __level, __optname, optval, __optlen)) < 0) {
        error(logt,"setsockopt failure.%s", strerror(errno));
        return FALSE;
    }
    return TRUE;
}
/**
 * 绑定封装处理
 * @param logt
 * @param __socket_fd
 * @param __sa_family
 * @param __listen_ip
 * @param __listen_port
 * @return
 */
static inline int __socket_bind(log_t *logt, int __socket_fd, const short __sa_family, const in_addr_t listen_addr, const int __listen_port) {
    int bindid;
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    //设置协议
    server_addr.sin_family = (!!__sa_family)?AF_INET:__sa_family;
    //设置Port
    // 端口, 网络协议都是小端序, 要用这个htons系列函数将host编码转为net编码,
    // intel机器都是小端, 所以一般都直接返回
    server_addr.sin_port = htons(__listen_port);
    if(!listen_addr) {   //设置IP
        // 同一台机器可能有多个网卡, 一个网卡也可以绑定多个IP, INADDR_ANY代表所有IP都绑定
        server_addr.sin_addr.s_addr = INADDR_ANY;
    } else {
        server_addr.sin_addr.s_addr = htonl(listen_addr);
    }
    memset(server_addr.sin_zero,'\0',sizeof(server_addr.sin_zero));
    if((bindid = bind(__socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))) == -1) {
        error(logt, "socket[%d] bind on port[%d] for ip address failure:%s", __socket_fd , __listen_port, strerror(errno));
        return FALSE;
    }
    return TRUE;
}
/**
 * 监听封装
 * @param logt
 * @param __socket_fd
 * @param __n
 * @return
 */
static inline int __socket_listen(log_t *logt, int __socket_fd, int __n) {
    int listenid;
    if((listenid = listen(__socket_fd, __n)) < 0)
    {
        error(logt,"socket[%d] listen() fail:%s", __socket_fd, strerror(errno));
        return FALSE;
    }
    return TRUE;
}
/**
 * 建立链接
 * @param logt
 * @param __socket_fd
 * @param __peer
 * @param __len
 * @return
 */
static inline int __socket_accept(log_t *logt, int __socket_fd, struct sockaddr *__peer, socklen_t *__len) {
    int client_fd = -1;
    if((client_fd = accept(__socket_fd, __peer, __len)) < 0) {
        error(logt,"accept new client failure:%s", strerror(errno));
        return FALSE;
    }
    return client_fd;
}
/**
 *
 * @param logt
 * @param __client_fd
 * @param buf
 * @param len
 * @param flags
 *  MSG_PEEK	recv 函数的使用标志 查看数据,并不从系统缓冲区移走数据
    MSG_OOB	    接受或者发送带外数据
    MSG_WAITALL	recv 函数的使用标志 等待所有数据
    MSG_DONTWAIT	仅本操作非阻塞
    MSG_DONTROUTE	 send 函数使用的标志 不查找表
 * @return
 */
// 发送消息
static inline int __send(log_t *logt, int __client_fd, char* buf, int len, unsigned int flags) {
    int rv = send(__client_fd, buf, len, flags);
    return rv;
}
// 接收消息
static inline int __recv(log_t *logt, int __client_fd, char* buf, int len, unsigned int flags) {
    int rv = recv(__client_fd, buf, len, flags);
    return rv;
}
// 接收消息
static inline int __read(log_t *logt, int __fd, char* buf, int len) {
    int rv = read(__fd, buf, len);
    return rv;
}
static inline void __closeAll(log_t *logt, int __socket_fd, int __client_fd) {
    if(__client_fd) close(__client_fd);
#if defined(__MINGW32__) || defined(__MINGW64__)
    //Winsows下关闭socket
    if(ISNOTNULL(__socket_fd)) closesocket(__socket_fd);
    WSACleanup();
#else
    if(__socket_fd) close(__socket_fd);
#endif
    if(__socket_fd) exit(EXIT_SUCCESS);
}
/**
 * 创建服务端
 * @param logt
 * @param listen_ip
 * @param listen_port
 * @param __family
 * @param __type
 * @param __protocol
 * @param __optlevel    选项定义的层次；支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
 * @param __optname     需设置的选项
 * @param optval        指针，指向存放选项待设置的新值的缓冲区
 * @param __optlen      缓冲区长度
 * @param n             链接队列 socket内部的队列长度 程序所能同时接收的最大链接数
 * @return
 */
static int _socket_server_create(log_t *logt, const in_addr_t listen_addr, int listen_port, int __family, int __type, int __protocol,
                               int __optlevel, int __optname, const void *optval,
                               socklen_t __optlen, int n) {
    int socket_fd;
    if(!(socket_fd = __socket_create(logt, __family, __type, __protocol)))
        return FALSE;
    info(logt, "socket[%d] create success", socket_fd);
    if(!__socket_option(logt, socket_fd, __optlevel, __optname, optval, __optlen))
        return FALSE;
    if((!__socket_bind(logt, socket_fd, __family, listen_addr, listen_port)))
        return FALSE;
    info(logt, "socket[%d] bind on port[%d] for ip address success", socket_fd, listen_port);
    if(!__socket_listen(logt, socket_fd, n) < 0)
        return FALSE;
    return socket_fd;
}

/**
 * 创建客户端连接
 * @param logt
 * @param addr
 * @param port
 * @return
 */
static inline int _socket_clinet_create(log_t *logt, const struct in_addr sin_addr, int port, int __family, int __type, int __protocol) {
    int socket_fd;
    if(!(socket_fd = __socket_create(logt, __family, __type, __protocol)))
        return FALSE;
    struct sockaddr_in c_addr;
    memset(&c_addr,0, sizeof(struct sockaddr_in));

    c_addr.sin_family = __family;
    c_addr.sin_port   = htons(port);
    c_addr.sin_addr = sin_addr;
    if(__socket_connect(logt, socket_fd, &c_addr)){
        return socket_fd;
    }
    return FALSE;
}
static int _createHttpSocket(log_t *logt, const in_addr_t listen_addr, int listen_port, int link_num)
{
    int on = 1;
#if defined(__MINGW32__) || defined(__MINGW64__)
    //Winsows下启用socket
    int nRes;
    WSADATA wsadata;
    if(WSAStartup(MAKEWORD(2,2),&wsadata)==SOCKET_ERROR)
    {
      info(logt,"WSAStartup() fail");
      exit(0);
    }

    if (0 != nRes)
    {
        switch (nRes)
        {
            case WSASYSNOTREADY:
                error(logt, "可以重启电脑，或检查网络库");
                break;
            case WSAVERNOTSUPPORTED:
                error(logt, "请更新网络库");
                break;
            case WSAEINPROGRESS:
                error(logt, "Please reboot this software");
                break;
            case WSAEPROCLIM:
                error(logt, "请关闭不必要的软件，以为当前网络提供充足资源");
                break;
            case WSAEFAULT:
                error(logt, "参数错误");
                break;
        }
    }
    //版本校验
    if (2 != HIBYTE(wsadata.wVersion) || 2 != LOBYTE(wsadata.wVersion))
    {
        //版本打开错误
        error(logt, "WSAStartup 初始化失败：版本打开错误");
        CloseAll(logt, 0, 0);		//关闭网络库
        return 0;
    }
#endif
    return _socket_server_create(logt, listen_addr, listen_port, AF_INET, SOCK_STREAM,
                               0, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on), link_num);
}

static int _createHttpClient(log_t *logt, const struct in_addr sin_addr, int port) {
    return _socket_clinet_create(logt, sin_addr, port, AF_INET, SOCK_STREAM, 0);
}

int CreateHttpSocket(log_t *logt, const in_addr_t listen_addr, int listen_port, int link_num)
{
    return _createHttpSocket(logt, listen_addr, listen_port, link_num);
}
int CreateHttpClient(log_t *logt, const struct in_addr sin_addr, int port)
{
    return _createHttpClient(logt, sin_addr, port);
}
int Accept(log_t *logt, int socket_fd, struct sockaddr *saddr, socklen_t *slen) {
    return __socket_accept(logt, socket_fd, saddr, slen);
}

int putData(log_t *logt,const int client_fd, char* buf, int len) {
    return __send(logt, client_fd, buf, len, 0);
}

int getData(log_t *logt, const int client_fd, char* buf, int len) {
    return __recv(logt, client_fd, buf, len, 0);
}
/**
 * 虚读
 * @info: 一个流式（TCP）套接字而言，指定 MSG_PEEK 两次调用 recv，返回值可能不一样，因为这两次调用之间 TCP 可能又收到了一些数据。
 * @param logt
 * @param client_fd
 * @param buf
 * @param len
 * @return
 */
int vgetData(log_t *logt, const int client_fd, char* buf, int len) {
    return __recv(logt, client_fd, buf, len, MSG_PEEK);
}
/**
 * 客户端异常退出导致服务端send函数崩溃,忽略sigpipe
 * @param logt
 * @param client_fd
 * @param buf
 * @param len
 * @return
 */
int noSgPutData(log_t *logt,const int client_fd, char* buf, int len) {
    return __send(logt, client_fd, buf, len, MSG_NOSIGNAL);
}
/**
 * 正常写
 * @param logt
 * @param client_fd
 * @param buf
 * @param len
 * @return
 */
int Write(log_t *logt,const int client_fd, char* buf, int len) {
    return __send(logt, client_fd, buf, len, 0);
}
/**
 * 正常读
 * @param logt
 * @param client_fd
 * @param buf
 * @param len
 * @return
 */
int Recv(log_t *logt,const int client_fd, char* buf, int len) {
    return __recv(logt, client_fd, buf, len, 0);
}

/**
 * 读标识符
 * @param logt
 * @param fd
 * @param buf
 * @param len
 * @return
 */
int Read(log_t *logt, const int fd, char* buf, int len) {
    return __read(logt, fd, buf, len);
}

int __socket_set_block(log_t *logt, int fd, int non_block) {
#if !defined(__MINGW32__) && !defined(__MINGW64__)
    int flags;

    /* Set the socket blocking (if non_block is zero) or non-blocking.
     * Note that fcntl(2) for F_GETFL and F_SETFL can't be
     * interrupted by a signal. */
    if ((flags = fcntl(fd, F_GETFL)) == -1) {
        error(logt, "fcntl : %s", strerror(errno));
        return FALSE;
    }

    /* Check if this flag has been set or unset, if so,
     * then there is no need to call fcntl to set/unset it again. */
    if (!!(flags & O_NONBLOCK) == !!non_block)
        return TRUE;

    if (non_block)
        flags |= O_NONBLOCK;
    else
        flags &= ~O_NONBLOCK;

    if (fcntl(fd, F_SETFL, flags) == -1) {
        error(logt, "fcntl : %s", strerror(errno));
        return FALSE;
    }
    return TRUE;
#else
    return FALSE;
#endif
}

// 获取一个已完成三次握手的连接
int connAccept(log_t *logt, http_socket_event_handle_t *handler, int fd) {
    while (1) {
        int conn_fd = Accept(logt, fd, (struct sockaddr *)&handler->client_addr, &handler->sin_size);
        if(conn_fd <= 0) {
            if(errno == EAGAIN || errno == EWOULDBLOCK) {
                continue;
            }
            error(logt, "one client link fail[%d]", conn_fd);
            return FALSE;
        }
        return conn_fd;
    }
}
/**
 * 设置文件描述符为非阻塞
 * @param logt
 * @param fd
 * @return
 */
int socketNonBlock(log_t *logt, int fd) {
    return __socket_set_block(logt, fd,1);
}

/**
 * 设置文件描述符为阻塞
 * @param logt
 * @param fd
 * @return
 */
int socketBlock(log_t *logt, int fd) {
    return __socket_set_block(logt, fd,0);
}
void CloseAll(log_t *logt, int socket_fd, int client_fd) {
    __closeAll(logt, socket_fd, client_fd);
}